import { EDITOR } from "cc/env";
import c3d from "../../../cc";

/*
 * @features: 功能
 * @description: 说明
 * @Date: 2021-10-07 20:42:26
 * @Author: judu233(769471424@qq.com)
 * @LastEditTime: 2021-10-27 22:23:00
 * @LastEditors: judu233
 */

declare module "cc" {
    export interface Component {
        /**组件事件, 直接new Map 设置，在onLoad时会自动注册,onDestroy注销*/
        comEvent: Map<keyof IEventKey, broadcast.Listener<any, any>>;
        /**
         * 通过函数名字调用节点上有该方法的组件上的函数
         * @param node 节点
         * @param funName 函数名
         * @param params 参数
         */
        applyComponentsFunction(node: c3d.Node, funName: string, params: any): void;
        /**
         * 拷贝一个事件句柄
         * @param event 事件句柄
         */
        copyEventHandle(event: c3d.EventHandler): c3d.EventHandler;
        // /**
        //  * 检查一个事件句柄（）
        //  * @param event 事件句柄
        //  */
        // checkEventHandle(event: c3d.EventHandler): boolean;
        /**
         * 创建一个事件句柄
         * @param node 节点
         * @param comName 组件名字
         * @param callFunName 组件函数string
         * @param customEventData 自定义参数
         */
        createEventHandle<T extends c3d.Component>(node: c3d.Node, comName: string, callFunName: keyof T, customEventData?: any): c3d.EventHandler;

        /**添加 comEvent里的事件， 会在onLoad时自动调用*/
        addEventListeners(): void;
        /**移除comEvent里的事件 ， 会在onDestory自动调用*/
        removeEventListeners(): void;

        /***********按钮事件注册******************** */

        /**onload时注册事件 */
        regEvent(): void;
        /**onDestory取消事件 */
        unRegEvent(): void;

        /**
         * 当按钮抬起来时触发，只有设置_enabledKeyUp为true生效
         * @param ev event
         */
        onKeyUp(ev: c3d.EventKeyboard): void;
        /**
         * 当按钮按下时触发，只有设置_enabledKeyDown为true生效
         * @param ev event
         */
        onKeyDown(ev: c3d.EventKeyboard): void;
        /**鼠标滑轮滚动时 */
        onMouseWheel(): void;
        /**鼠标抬起时 */
        onMouseDown(): void;
        /**鼠标按下时 */
        onMouseEnter(): void;
        /**手指按下时执行 */
        onTouchStart(): void;
        /**手指移动时执行 */
        onTouchMove(): void;
        /**手指在节点内抬起时执行 */
        onTouchNodeEnd(): void;
        /**手指在节点外抬起时执行 */
        onTouchNodeCancel(): void;
        /**手指抬起时执行 */
        onTouchEnd(): void;
        /**子节点添加时 */
        onAddChild(): void;
        /**子节点移除时时 */
        onRemoveChild(): void;
        /**节点尺寸改变时 */
        onSizeChange(): void;

        /**启用按键抬起按下事件 */
        eableKeyTouch(): void;
        /**启用触摸相关的事件 */
        eableTouch(): void;
        /**禁止按键抬起按下事件 */
        disableKeyTouch(): void;
        /**禁止所有触摸事件 */
        disableTouch(): void;
    }
}

export class ComponentEventExtends {

    //#region  

    static applyComponentsFunction(node: c3d.Node, funName: string, params: any) {
        for (let i = 0; i < node.components.length; i++) {
            let component: any = node.components[i];
            let func = component[funName];
            if (func) {
                func.call(component, params);
            }
        }
    }

    static createEventHandle<T extends c3d.Component>(node: c3d.Node, comName: string, callFunName: keyof T, customEventData?: any) {
        let com = node.getComponent(comName) ?? node.getComponent(c3d.js.getClassByName(comName) as any);
        c3d.log(com)
        let clickHandle = new c3d.Component[`EventHandler`]();
        clickHandle.target = node;
        clickHandle.component = comName; //这个脚本的类名
        clickHandle.handler = callFunName as string;
        clickHandle.customEventData = customEventData ?? '';
        clickHandle['_componentId'] = com[`__cid__`];
        return clickHandle;
    }

    static copyEventHandle(event: c3d.EventHandler) {
        //克隆数据，每个节点获取的都是不同的回调
        let hd = new c3d.Component['EventHandler']();//copy对象
        hd.target = event.target;
        hd.handler = event.handler;
        hd.component = event.component;
        hd['_componentId'] = event['_componentId'];
        return hd;
    }

    static checkEventHandle(event: c3d.EventHandler) {
        var target = event.target;
        if (!c3d.isValid(target))
            return false;

        event["_genCompIdIfNeeded"]();
        let compType = c3d.js[`_getClassById`](event["_componentId"]) as any;

        let comp = target.getComponent(compType);
        if (!c3d.isValid(comp))
            return false;

        let handler = comp[event.handler] as Function;
        if (typeof (handler) !== `function`)
            return false;

        if (handler.call(comp, event.customEventData))
            return true;
        return false;
    }
    //#endregion

    /***********关于事件注册***********/
    //#region 

    static addEventListeners() {
        let self = this as any as c3d.Component;
        self.comEvent?.forEach((func, name) => {
            GM.eventMgr.ins.on(name, func, this);
        });
    }

    static removeEventListeners() {
        let self = this as any as c3d.Component;
        if (self.comEvent && self.comEvent.size > 0) {
            GM.eventMgr.ins.offAllByContext(this);
            self.comEvent?.clear();
        }
    }

    /**注册事件 */
    static regEvent() {
        this.eableKeyTouch();
        this.eableTouch();
    }

    /**取消注册事件 */
    static unRegEvent() {
        this.disableKeyTouch();
        this.disableTouch();
    }

    static eableKeyTouch() {
        let self = this as unknown as c3d.Component;
        self.onKeyUp && c3d.systemEvent.on(c3d.SystemEvent.EventType.KEY_UP, self.onKeyUp, this);
        self.onKeyDown && c3d.systemEvent.on(c3d.SystemEvent.EventType.KEY_DOWN, self.onKeyDown, this);
        self.onMouseWheel && self.node.on(c3d.Node.EventType.MOUSE_WHEEL, self.onMouseWheel, this);
        self.onMouseDown && self.node.on(c3d.Node.EventType.MOUSE_DOWN, self.onMouseDown, this);
        self.onMouseEnter && self.node.on(c3d.Node.EventType.MOUSE_ENTER, self.onMouseEnter, this);
    }

    static disableKeyTouch() {
        let self = this as unknown as c3d.Component;
        self.onKeyUp && c3d.systemEvent.off(c3d.SystemEvent.EventType.KEY_UP, self.onKeyUp, this);
        self.onKeyDown && c3d.systemEvent.off(c3d.SystemEvent.EventType.KEY_DOWN, self.onKeyDown, this);
        self.onMouseWheel && self.node.off(c3d.Node.EventType.MOUSE_WHEEL, self.onMouseWheel, this);
        self.onMouseDown && self.node.off(c3d.Node.EventType.MOUSE_DOWN, self.onMouseDown, this);
        self.onMouseEnter && self.node.off(c3d.Node.EventType.MOUSE_ENTER, self.onMouseEnter, this);
    }

    static eableTouch() {
        let self = this as unknown as c3d.Component;
        self.onTouchStart && self.node.on(c3d.Node.EventType.TOUCH_START, self.onTouchStart, this);
        self.onTouchMove && self.node.on(c3d.Node.EventType.TOUCH_MOVE, self.onTouchMove, this);
        self.onTouchNodeEnd && self.node.on(c3d.Node.EventType.TOUCH_END, self.onTouchNodeEnd, this);
        self.onTouchNodeCancel && self.node.on(c3d.Node.EventType.TOUCH_CANCEL, self.onTouchNodeCancel, this);
        self.onTouchEnd && self.node.on(c3d.Node.EventType.TOUCH_END, self.onTouchEnd, this);
        self.onTouchEnd && self.node.on(c3d.Node.EventType.TOUCH_CANCEL, self.onTouchEnd, this);

        self.onAddChild && self.node.on(c3d.Node.EventType.CHILD_ADDED, self.onAddChild, this);
        self.onRemoveChild && self.node.on(c3d.Node.EventType.CHILD_REMOVED, self.onRemoveChild, this);
        self.onSizeChange && self.node.on(c3d.Node.EventType.SIZE_CHANGED, self.onSizeChange, this);
    }

    static disableTouch() {
        let self = this as unknown as c3d.Component;
        self.onTouchStart && self.node.off(c3d.Node.EventType.TOUCH_START, self.onTouchStart, this);
        self.onTouchMove && self.node.off(c3d.Node.EventType.TOUCH_MOVE, self.onTouchMove, this);
        self.onTouchNodeEnd && self.node.off(c3d.Node.EventType.TOUCH_END, self.onTouchNodeEnd, this);
        self.onTouchNodeCancel && self.node.off(c3d.Node.EventType.TOUCH_CANCEL, self.onTouchNodeCancel, this);
        self.onTouchEnd && self.node.off(c3d.Node.EventType.TOUCH_END, self.onTouchEnd, this);
        self.onTouchEnd && self.node.off(c3d.Node.EventType.TOUCH_CANCEL, self.onTouchEnd, this);

        self.onAddChild && self.node.off(c3d.Node.EventType.CHILD_ADDED, self.onAddChild, this);
        self.onRemoveChild && self.node.off(c3d.Node.EventType.CHILD_REMOVED, self.onRemoveChild, this);
        self.onSizeChange && self.node.off(c3d.Node.EventType.SIZE_CHANGED, self.onSizeChange, this);
    }
    //#endregion
}

//添加onload ,ondestory事件控制
if (!EDITOR) {
    //@ts-ignore
    let onLoad = c3d.Component.prototype.onLoad;
    //@ts-ignore
    c3d.Component.prototype.onLoad = function () {
        let self = this as any as c3d.Component;
        self.addEventListeners();
        onLoad?.bind(this, arguments);
    }

    //@ts-ignore
    let onEable = c3d.Component.prototype.onEnable;
    //@ts-ignore
    c3d.Component.prototype.onEnable = function () {
        let self = this as any as c3d.Component;
        self.regEvent();
        onEable?.bind(this, arguments);
    }

    //@ts-ignore
    let onDisEable = c3d.Component.prototype.onDisable;
    //@ts-ignore
    c3d.Component.prototype.onDisable = function () {
        let self = this as any as c3d.Component;
        self.unRegEvent();
        onDisEable?.bind(this, arguments);
    }

    //@ts-ignore
    let onDestory = c3d.Component.prototype.onDestroy;
    //@ts-ignore
    c3d.Component.prototype.onDestroy = function () {
        let self = this as any as c3d.Component;
        self.removeEventListeners();
        onDestory?.bind(this, arguments);
    }
}